{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pwlf\n",
    "pwlf.__version__\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Force minimum length of segments to be greater than x\n",
    "\n",
    "[Issue 66](https://github.com/cjekel/piecewise_linear_fit_py/issues/66)\n",
    "\n",
    "This jupyter notebook uses SLSQP to apply a constraint function to force a minimum line segment length. You'll need to supply a starting point (or guess) to start the optimization. If you don't know what is a good starting point, check out how I use Latin Hypercube random sampling to run multiple optimizations in the ```fitfast``` function. \n",
    "\n",
    "\n",
    "A constraint function could look like:\n",
    "```python\n",
    "def my_con(var):\n",
    "    var = np.sort(var)\n",
    "    distances = np.zeros(number_of_line_segments)\n",
    "    distances[0] = var[0] - my_pwlf.break_0\n",
    "    distances[-1] = my_pwlf.break_n - var[-1]\n",
    "    for i in range(number_of_line_segments - 2):\n",
    "        distances[i+1] = var[i+1] - var[i]\n",
    "    # element must be greater or equal to 0.0\n",
    "    # in a successfully optimized problem\n",
    "    return np.array((distances.min() - min_length))\n",
    "```\n",
    "\n",
    "This is a single constraint for the minimum length of all segments. It's possible that the ```min()``` in this function will create issues with the gradient of the constraint. If you run into issues with this, you may want to investigate using a separate constraint for each line segment. That could be done by changing:\n",
    "```python\n",
    "    return np.array((distances.min() - min_length))\n",
    "```\n",
    "to\n",
    "```python\n",
    "    return distances - min_length\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# your data\n",
    "y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02,\n",
    "              4.39052750e-02, 5.45343950e-02, 6.74104940e-02,\n",
    "              8.34831790e-02, 1.02580042e-01, 1.22767939e-01,\n",
    "              1.42172312e-01, 0.00000000e+00, 8.58600000e-06,\n",
    "              8.31543400e-03, 2.34184100e-02, 3.39709150e-02,\n",
    "              4.03581990e-02, 4.53545600e-02, 5.02345260e-02,\n",
    "              5.55253360e-02, 6.14750770e-02, 6.82125120e-02,\n",
    "              7.55892510e-02, 8.38356810e-02, 9.26413070e-02,\n",
    "              1.02039790e-01, 1.11688258e-01, 1.21390666e-01,\n",
    "              1.31196948e-01, 0.00000000e+00, 1.56706510e-02,\n",
    "              3.54628780e-02, 4.63739040e-02, 5.61442590e-02,\n",
    "              6.78542550e-02, 8.16388310e-02, 9.77756110e-02,\n",
    "              1.16531753e-01, 1.37038283e-01, 0.00000000e+00,\n",
    "              1.16951050e-02, 3.12089850e-02, 4.41776550e-02,\n",
    "              5.42877590e-02, 6.63321350e-02, 8.07655920e-02,\n",
    "              9.70363280e-02, 1.15706975e-01, 1.36687642e-01,\n",
    "              0.00000000e+00, 1.50144640e-02, 3.44519970e-02,\n",
    "              4.55907760e-02, 5.59556700e-02, 6.88450940e-02,\n",
    "              8.41374060e-02, 1.01254006e-01, 1.20605073e-01,\n",
    "              1.41881288e-01, 1.62618058e-01])\n",
    "x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02,\n",
    "              5.66106800e-02, 7.95549800e-02, 1.00936330e-01,\n",
    "              1.20351520e-01, 1.37442010e-01, 1.51858250e-01,\n",
    "              1.64433570e-01, 0.00000000e+00, -2.12600000e-05,\n",
    "              7.03872000e-03, 1.85494500e-02, 3.00926700e-02,\n",
    "              4.17617000e-02, 5.37279600e-02, 6.54941000e-02,\n",
    "              7.68092100e-02, 8.76596300e-02, 9.80525800e-02,\n",
    "              1.07961810e-01, 1.17305210e-01, 1.26063930e-01,\n",
    "              1.34180360e-01, 1.41725010e-01, 1.48629710e-01,\n",
    "              1.55374770e-01, 0.00000000e+00, 1.65610200e-02,\n",
    "              3.91016100e-02, 6.18679400e-02, 8.30997400e-02,\n",
    "              1.02132890e-01, 1.19011260e-01, 1.34620080e-01,\n",
    "              1.49429370e-01, 1.63539960e-01, -0.00000000e+00,\n",
    "              1.01980300e-02, 3.28642800e-02, 5.59461900e-02,\n",
    "              7.81388400e-02, 9.84458400e-02, 1.16270210e-01,\n",
    "              1.31279040e-01, 1.45437090e-01, 1.59627540e-01,\n",
    "              0.00000000e+00, 1.63404300e-02, 4.00086000e-02,\n",
    "              6.34390200e-02, 8.51085900e-02, 1.04787860e-01,\n",
    "              1.22120350e-01, 1.36931660e-01, 1.50958760e-01,\n",
    "              1.65299640e-01, 1.79942720e-01])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# initialize piecewise linear fit with your x and y data\n",
    "my_pwlf = pwlf.PiecewiseLinFit(x, y)\n",
    "\n",
    "# initialize custom optimization\n",
    "number_of_line_segments = 3\n",
    "my_pwlf.use_custom_opt(number_of_line_segments)\n",
    "\n",
    "# minium length of a segment\n",
    "min_length = 0.05"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_con(var):\n",
    "    var = np.sort(var)\n",
    "    distances = np.zeros(number_of_line_segments)\n",
    "    distances[0] = var[0] - my_pwlf.break_0\n",
    "    distances[-1] = my_pwlf.break_n - var[-1]\n",
    "    for i in range(number_of_line_segments - 2):\n",
    "        distances[i+1] = var[i+1] - var[i]\n",
    "    # element must be greater or equal to 0.0\n",
    "    # in a successfully optimized problem\n",
    "    return np.array((distances.min() - min_length))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization terminated successfully.    (Exit mode 0)\n",
      "            Current function value: 0.00035834090548162784\n",
      "            Iterations: 4\n",
      "            Function evaluations: 18\n",
      "            Gradient evaluations: 4\n"
     ]
    }
   ],
   "source": [
    "from scipy.optimize import fmin_slsqp\n",
    "# i have number_of_line_segments - 1 number of variables\n",
    "# let's guess the correct location of the two unknown variables\n",
    "# (the program defaults to have end segments at x0= min(x)\n",
    "# and xn=max(x)\n",
    "xGuess = np.zeros(number_of_line_segments - 1)\n",
    "xGuess[0] = 0.06\n",
    "xGuess[1] = 0.13\n",
    "bounds = np.zeros((number_of_line_segments - 1, 2))\n",
    "bounds[:, 0] = my_pwlf.break_0\n",
    "bounds[:, 1] = my_pwlf.break_n\n",
    "\n",
    "res = fmin_slsqp(my_pwlf.fit_with_breaks_opt, xGuess, f_ieqcons=my_con,\n",
    "                 bounds=bounds, iter=100, acc=1e-06, iprint=1,\n",
    "                 epsilon=1.4901161193847656e-08)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# set up the break point locations\n",
    "x0 = np.zeros(number_of_line_segments + 1)\n",
    "x0[0] = np.min(x)\n",
    "x0[-1] = np.max(x)\n",
    "x0[1:-1] = res\n",
    "\n",
    "# calculate the parameters based on the optimal break point locations\n",
    "my_pwlf.fit_with_breaks(x0)\n",
    "\n",
    "# predict for the determined points\n",
    "xHat = np.linspace(min(x), max(x), num=10000)\n",
    "yHat = my_pwlf.predict(xHat)\n",
    "\n",
    "plt.figure()\n",
    "plt.plot(x, y, 'o')\n",
    "plt.plot(xHat, yHat, '-')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
